<?php
/**
 * Fastapp.
 * 2/2/24
 * @Author deepsea(wx:deepsea_0024)
 * @Contact telegram:deepsea159
 */

namespace App\Fastapp\Exception;

use Hyperf\Codec\Json;
use Hyperf\Contract\StdoutLoggerInterface;
use Hyperf\Database\Model\ModelNotFoundException;
use Hyperf\ExceptionHandler\ExceptionHandler;
use Hyperf\HttpMessage\Stream\SwooleStream;
use Hyperf\Logger\Logger;
use Hyperf\Logger\LoggerFactory;
use Hyperf\RateLimit\Exception\RateLimitException;
use Mine\Exception\TokenException;
use Mine\Helper\MineCode;
use Mine\Log\RequestIdHolder;
use Psr\Http\Message\ResponseInterface;
use Throwable;
use Xmo\JWTAuth\Exception\JWTException;

class FastAppExceptionHandle extends ExceptionHandler
{
    protected Logger $logger;

    protected StdoutLoggerInterface $console;

    public function __construct()
    {
        $this->console = console();
        $this->logger = container()->get(LoggerFactory::class)->get('fastapp');
    }

    public function handle(Throwable $throwable, ResponseInterface $response): ResponseInterface
    {
        $this->stopPropagation();
        $msg = $throwable->getMessage();
        $code = $throwable->getCode();
        $status = 200;

        if ($throwable instanceof TipException) {
            $code = $throwable->getCode() ?: 5001;
        } elseif ($throwable instanceof ModelNotFoundException) {
            $code = 5003;
            $msg = '数据不存在';
        } elseif ($throwable instanceof TokenException || $throwable instanceof JWTException) {
            $code = MineCode::TOKEN_EXPIRED;
            $status = 401;
        } elseif ($throwable instanceof RateLimitException) {
            $code = 5002;
            $msg = '服务繁忙';
        } else {
            $this->logger->error(sprintf('%s[%s] in %s', $msg, $throwable->getLine(), $throwable->getFile()));
            $this->console->error(sprintf('%s[%s] in %s', $msg, $throwable->getLine(), $throwable->getFile()));
//            $this->console->error($throwable->getTraceAsString());
            sys_notify(sprintf('%s[%s] in %s', $msg, $throwable->getLine(), $throwable->getFile()));
        }
        $format = [
            'requestId' => RequestIdHolder::getId(),
            'success' => false,
            'code' => $code,
            'message' => $msg,
            'data' => new \stdClass()
        ];
        return $response->withHeader('Server', 'Fastapp')
            ->withAddedHeader('content-type', 'application/json; charset=utf-8')
            ->withStatus($status)->withBody(new SwooleStream(Json::encode($format)));
    }

    public function isValid(Throwable $throwable): bool
    {
        return true;
    }

}